﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using Ignitron.EEngine.Maths;
using Ignitron.EEngine.Lighting;
using Ignitron.EEngine.Model;
using Ignitron.EEngine.Adapters;

namespace Ignitron.EEngine
{
    /// <summary>
    /// Enumeration for engine platform
    /// </summary>
    public enum eEnginePlatform { DirectX11, OpenGL, Probe };

    /// <summary>
    /// The main 3D engine wrapper class
    /// </summary>
    public class Engine: IEngine
    {
        /// <summary>
        /// The platform engine is running on
        /// </summary>
        private eEnginePlatform mPlatform;

        /// <summary>
        /// The platform adapter
        /// </summary>
        private IEngine mAdapter;

        /// <summary>
        /// Property getter for the platform used
        /// </summary>
        public eEnginePlatform Platform
        {
            get { return mPlatform; }
        }

        /// <summary>
        /// Performs initialization of the engine
        /// </summary>
        /// <param name="platform"></param>
        public void Initialize(eEnginePlatform platform)
        {
            mPlatform = platform;
            mAdapter = CreateAdapter(platform);
            mAdapter.RenderRequestEvent += Adapter_RenderRequestEvent;

            // bind the mouse events
            mAdapter.LeftButtonDownEvent += Adapter_LeftButtonDownEvent;
            mAdapter.LeftButtonUpEvent += Adapter_LeftButtonUpEvent;
            mAdapter.RightButtonDownEvent += Adapter_RightButtonDownEvent;
            mAdapter.RightButtonUpEvent += Adapter_RightButtonUpEvent;
            mAdapter.MouseMoveEvent += Adapter_MouseMoveEvent;
            mAdapter.MouseWheelEvent += Adapter_MouseWheelEvent;
        }
        
        /// <summary>
        /// True flag, when the initialization process of the engine is completed
        /// </summary>
        public bool IsInitializationCompleted
        {
            get { return mAdapter.IsInitializationCompleted; }
        }


        /// <summary>
        /// Closes the engine
        /// </summary>
        public void Close()
        {
            // unbind the events
            mAdapter.RenderRequestEvent -= Adapter_RenderRequestEvent;

            mAdapter.LeftButtonDownEvent -= Adapter_LeftButtonDownEvent;
            mAdapter.LeftButtonUpEvent -= Adapter_LeftButtonUpEvent;
            mAdapter.RightButtonDownEvent -= Adapter_RightButtonDownEvent;
            mAdapter.RightButtonUpEvent -= Adapter_RightButtonUpEvent;
            mAdapter.MouseMoveEvent -= Adapter_MouseMoveEvent;
            mAdapter.MouseWheelEvent -= Adapter_MouseWheelEvent;

            // invoke closing on the adapter
            mAdapter.Close();
        }

        /// <summary>
        /// Creates the engine adapter for the given platform
        /// </summary>
        private IEngine CreateAdapter(eEnginePlatform platform)
        {
            switch (platform)
            {
                case eEnginePlatform.DirectX11:
                    return new SharpDXAdapter();
                case eEnginePlatform.OpenGL:
                    return new OpenTKAdapter();
                default:
                    throw new InvalidOperationException();
            }
        }

        #region IEngine interface implementation

        /// <summary>
        /// Returns the control
        /// </summary>
        public DependencyObject Control
        {
            get { return mAdapter.Control; }
        }

        /// <summary>
        /// Rendering request event
        /// </summary>
        public event EventHandler RenderRequestEvent;

        /// <summary>
        /// Mouse handling events
        /// </summary>
        public event EventHandler<EMouseEventArgs> LeftButtonDownEvent;
        public event EventHandler<EMouseEventArgs> LeftButtonUpEvent;
        public event EventHandler<EMouseEventArgs> RightButtonDownEvent;
        public event EventHandler<EMouseEventArgs> RightButtonUpEvent;
        public event EventHandler<EMouseEventArgs> MouseMoveEvent;
        public event EventHandler<EMouseEventArgs> MouseWheelEvent;


        /// <summary>
        /// Invokes the rerendering
        /// </summary>
        public void InvokeRendering()
        {
            if (RenderRequestEvent != null)
            {
                RenderRequestEvent(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Returns the size of the rendering control
        /// </summary>
        public Size GetViewportSize()
        {
            return mAdapter.GetViewportSize();
        }

        /// <summary>
        /// Returns the world matrix
        /// </summary>
        public EMatrix4 GetWorldMatrix()
        {
            return mAdapter.GetWorldMatrix();
        }

        /// <summary>
        /// Returns the view matrix
        /// </summary>
        public EMatrix4 GetViewMatrix()
        {
            return mAdapter.GetViewMatrix();
        }

        /// <summary>
        /// Returns the projection matrix
        /// </summary>
        public EMatrix4 GetProjectionMatrix()
        {
            return mAdapter.GetProjectionMatrix();
        }

        /// <summary>
        /// Captures the mouse
        /// </summary>        
        public void SetCapture()
        {
            mAdapter.SetCapture();
        }

        /// <summary>
        /// Releases the mouse capture
        /// </summary>
        public void ReleaseCapture()
        {
            mAdapter.ReleaseCapture();
        }

        /// <summary>
        /// Prepares the backbuffer to render the frame
        /// </summary>
        public void BeginRender()
        {
            mAdapter.BeginRender();
        }

        /// <summary>
        /// Terminates the rendering and swaps the buffers
        /// </summary>
        public void EndRender()
        {
            mAdapter.EndRender();
        }

        /// <summary>
        /// Begins rendering points
        /// </summary>
        public void BeginPoints()
        {
            mAdapter.BeginPoints();
        }

        /// <summary>
        /// Ends rendering points
        /// </summary>
        public void EndPoints()
        {
            mAdapter.EndPoints();            
        }

        /// <summary>
        /// Begins rendering lines
        /// </summary>
        public void BeginLines()
        {
            mAdapter.BeginLines();
        }

        /// <summary>
        /// Ends rendering lines
        /// </summary>
        public void EndLines()
        {
            mAdapter.EndLines();
        }


       
        /// <summary>
        /// Setups the camera settings
        /// </summary>
        public void SetCamera(EVector3 position, EVector3 lookAt, EVector3 up)
        {
            mAdapter.SetCamera(position, lookAt, up);
        }

        /// <summary>
        /// Sets the camera's field of view
        /// </summary>
        public void SetFOV(float fov)
        {
            mAdapter.SetFOV(fov);
        }

        /// <summary>
        /// Sets the frustrum near and far values
        /// </summary>
        public void SetNearFarLimits(float near, float far)            
        {
            mAdapter.SetNearFarLimits(near, far);
        }


        /// <summary>
        /// Renders a point
        /// </summary>
        public void RenderPoint(EVector3 p0, EVector4 color)
        {
            mAdapter.RenderPoint(p0, color);
        }

        /// <summary>
        /// Renders a line
        /// </summary>
        public void RenderLine(EVector3 p1, EVector3 p2, EVector4 color)
        {
            mAdapter.RenderLine(p1, p2, color);
        }

        /// <summary>
        /// Renders a model
        /// </summary>
        public void RenderModel(EModel model)
        {
            mAdapter.RenderModel(model);
        }

        /// <summary>
        /// Renders normal vectors for a given vectors
        /// </summary>
        public void RenderNormals(EModel model, float magnitude, EVector4 color)
        {
            mAdapter.RenderNormals(model, magnitude, color);
        }

        /// <summary>
        /// Creates a model
        /// </summary>
        public EModel CreateModel()
        {
            return mAdapter.CreateModel();
        }

        /// <summary>
        /// Factory method for creating a texture
        /// </summary>
        public ETexture CreateTexture()
        {
            return mAdapter.CreateTexture();
        }


        /// <summary>
        /// Pushes the current matrix
        /// </summary>
        public void PushMatrix()
        {
            mAdapter.PushMatrix();
        }

        /// <summary>
        /// Multiplies the current matrix
        /// </summary>
        public void MultMatrix(ref EMatrix4 matrix)
        {
            mAdapter.MultMatrix(ref matrix);
        }

        /// <summary>
        /// Makes the current matrix identity
        /// </summary>
        public void LoadIdentity()
        {
            mAdapter.LoadIdentity();
        }

        /// <summary>
        /// Pops a matrix from the stack
        /// </summary>
        public void PopMatrix()
        {
            mAdapter.PopMatrix();
        }

        /// <summary>
        /// Sets the alpha blend value
        /// </summary>
        public void SetAlphaBlend(float alpha)
        {
            mAdapter.SetAlphaBlend(alpha);
        }

        /// <summary>
        /// Sets the current rendering material parameters for the mesh
        /// </summary>
        public void SetMaterial(EMaterial material)
        {
            mAdapter.SetMaterial(material);
        }

        /// <summary>
        /// Sets the current texture
        /// </summary>
        public void SetTexture(ETexture texture)
        {
            mAdapter.SetTexture(texture);
        }

        /// <summary>
        /// Sets the back buffer clearing color
        /// </summary>
        public void SetClearColor(EVector4 color)
        {
            mAdapter.SetClearColor(color);
        }

        /// <summary>
        /// Sets the rendered view's background image
        /// </summary>
        public void SetBackground(IBackground background)
        {
            mAdapter.SetBackground(background);
        }

        /// <summary>
        /// Enables or disables lighting
        /// </summary>
        public void EnableLighting(bool enabled)
        {
            mAdapter.EnableLighting(enabled);
        }

        /// <summary>
        /// Creates a new light
        /// </summary>
        public ELight CreateLight()
        {
            return mAdapter.CreateLight();
        }

        /// <summary>
        /// Renders a light
        /// </summary>
        public void RenderLight(ELight light)
        {
            mAdapter.RenderLight(light);
        }

        /// <summary>
        /// Sets the global ambient light
        /// </summary>
        public void SetGlobalAmbient(EVector3 color)
        {
            mAdapter.SetGlobalAmbient(color);
        }

        /// <summary>
        /// Initializes the rendering texture
        /// </summary>
        public void InitializeRenderingTexture(int width, int height)
        {
            mAdapter.InitializeRenderingTexture(width, height);
        }

        /// <summary>
        /// Begins rendering to texture
        /// </summary>
        public void BeginRenderToTexture()
        {
            mAdapter.BeginRenderToTexture();
        }

        /// <summary>
        /// Draws a texture to the rendering texture
        /// </summary>
        public void DrawTexture(ETexture texture, int x, int y, int width, int height)
        {
            mAdapter.DrawTexture(texture, x, y, width, height);
        }

        /// <summary>
        /// Draws a rect to the rendering texture
        /// </summary>
        public void DrawRect(int x, int y, int width, int height, EVector4 color)
        {
            mAdapter.DrawRect(x, y, width, height, color);
        }

        /// <summary>
        /// Ends drawing to the rendering texture
        /// </summary>
        public ETexture EndRenderToTexture()
        {
            return mAdapter.EndRenderToTexture();
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Handler request for re-rendering the scene
        /// </summary>
        private void Adapter_RenderRequestEvent(object sender, EventArgs e)
        {
            if (RenderRequestEvent != null)
            {
                RenderRequestEvent(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Left button pressed event
        /// </summary>
        private void Adapter_LeftButtonDownEvent(object sender, EMouseEventArgs e)
        {
            if (LeftButtonDownEvent != null)
            {
                LeftButtonDownEvent(this, e);
            }
        }


        /// <summary>
        /// Left button released event
        /// </summary>
        private void Adapter_LeftButtonUpEvent(object sender, EMouseEventArgs e)
        {
            if (LeftButtonUpEvent != null)
            {
                LeftButtonUpEvent(this, e);
            }
        }

        /// <summary>
        /// Mouse has moved event
        /// </summary>
        private void Adapter_MouseMoveEvent(object sender, EMouseEventArgs e)
        {
            if (MouseMoveEvent != null)
            {
                MouseMoveEvent(this, e);
            }
        }

        /// <summary>
        /// Right button pressed event
        /// </summary>
        private void Adapter_RightButtonDownEvent(object sender, EMouseEventArgs e)
        {
            if (RightButtonDownEvent != null)
            {
                RightButtonDownEvent(this, e);
            }
        }

        /// <summary>
        /// Right button released event
        /// </summary>
        private void Adapter_RightButtonUpEvent(object sender, EMouseEventArgs e)
        {
            if (RightButtonUpEvent != null)
            {
                RightButtonUpEvent(this, e);
            }
        }

        /// <summary>
        /// Event raised when the mouse wheel is rotated
        /// </summary>
        private void Adapter_MouseWheelEvent(object sender, EMouseEventArgs e)
        {
            if (MouseWheelEvent != null)
            {
                MouseWheelEvent(this, e);
            }
        }

        #endregion

    }
}
